package org.gpp.proj1.map.renderer;

import javax.swing.ImageIcon;

import org.gpp.proj1.map.combat.CombatMap;
import org.gpp.proj1.map.combat.Decoration;
import org.gpp.proj1.map.combat.ProceduralTexture;
import org.gpp.proj1.map.combat.SplatTexture;
import org.gpp.proj1.util.ModelFactory;

import com.jme.bounding.BoundingBox;
import com.jme.image.Texture;
import com.jme.math.Vector3f;
import com.jme.scene.Node;
import com.jme.scene.Spatial;
import com.jme.scene.state.TextureState;
import com.jme.system.DisplaySystem;
import com.jme.util.TextureManager;
import com.jmex.terrain.TerrainBlock;
import com.jmex.terrain.util.ImageBasedHeightMap;
import com.jmex.terrain.util.ProceduralSplatTextureGenerator;

/**
 * 
 * @author Gustavo de Farias
 */
public class CombatMapNodeBuilder {
	
	private static final float X_SCALE = 2;
	private static final float Z_SCALE = 2;
	
	private static final int TEXTURE_SIZE = 512;

	public static Spatial buildNode(CombatMap map, String nodeName) {
		
		// the carrier to the map
		Node mapNode = new Node(nodeName);
		
		// height map for terrain
		ImageBasedHeightMap heightMap = new ImageBasedHeightMap( new ImageIcon( map.heightMapFile ).getImage() );
		// scales for terrain
		Vector3f terrainScale = new Vector3f( CombatMapNodeBuilder.X_SCALE, map.heightMultiplier, CombatMapNodeBuilder.Z_SCALE );
		// create the terrain geometry
		TerrainBlock terrain = new TerrainBlock( nodeName + " Terrain", heightMap.getSize(), terrainScale, heightMap.getHeightMap(), new Vector3f(0, 0, 0), false );
		// bounds the geometry
		terrain.setModelBound(new BoundingBox());
		terrain.updateModelBound();
		// attach to node
		mapNode.attachChild(terrain);
		
		// create textures
		ProceduralSplatTextureGenerator textureGenerator = new ProceduralSplatTextureGenerator( heightMap );
		// main textures (procedural = height based)
		for (ProceduralTexture texture : map.proceduralTextures) {
			textureGenerator.addTexture( new ImageIcon(texture.texture), texture.low, texture.optimal, texture.high);
		}
		// over textures (splat = alpha mask based)
		if (map.splatTextures != null) {
			for (SplatTexture texture : map.splatTextures) {
				textureGenerator.addSplatTexture( new ImageIcon(texture.mask), new ImageIcon(texture.texture) );
			}
		}
		// unify texture layers
		textureGenerator.createTexture( CombatMapNodeBuilder.TEXTURE_SIZE );
		// set texture to terrain
		TextureState terrainState = DisplaySystem.getDisplaySystem().getRenderer().createTextureState();
		terrainState.setEnabled( true );
		terrainState.setTexture( TextureManager.loadTexture( textureGenerator.getImageIcon().getImage(), Texture.MM_LINEAR_LINEAR, Texture.FM_LINEAR, false ) );
		terrain.setRenderState(terrainState);
		
		// add decorations
		if (map.decorations != null) {
			for (Decoration decoration : map.decorations) {
				// decoration model
				Spatial decorationModel = ModelFactory.getInstance().getModel( decoration.model );

				// decoration texture
				if (decoration.texture != null) {
					TextureState decorationState = DisplaySystem.getDisplaySystem().getRenderer().createTextureState();
					decorationState.setEnabled( true );
					decorationState.setTexture( TextureManager.loadTexture( new ImageIcon(decoration.texture).getImage(), Texture.MM_LINEAR_LINEAR, Texture.FM_LINEAR, false ) );
					decorationModel.setRenderState(decorationState);
				}
				
				// translation
				Vector3f multipliedTranslation = new Vector3f(decoration.translation.x * CombatMapNodeBuilder.X_SCALE, 
															  decoration.translation.y,
															  decoration.translation.z * CombatMapNodeBuilder.Z_SCALE);
				decorationModel.setLocalTranslation(new Vector3f(multipliedTranslation.x,
																terrain.getHeight(multipliedTranslation) + multipliedTranslation.y,
																multipliedTranslation.z));
				// rotation
				decorationModel.setLocalRotation( decoration.rotation );
				// scale
				decorationModel.setLocalScale( decoration.scale );
				
				// attach to node
				mapNode.attachChild(decorationModel);
			}
		}
		
		return mapNode;
	}
}